/*
 * PCF8574 GPIO Port Expander for ESP32
 * Based on Arduino PCF8574 library by Renzo Mischianti
 * Adapted for ESP-IDF with custom I2cMaster class
 *
 * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: Apache-2.0
 */

#pragma once

#include <memory>
#include <functional>
#include <vector>
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"


#include "esp_i2c_master.h"

// PCF8574引脚定义
#define P0  0
#define P1  1
#define P2  2
#define P3  3
#define P4  4
#define P5  5
#define P6  6
#define P7  7

// 引脚模式定义（兼容Arduino）
#ifndef INPUT
#define INPUT           0
#endif
#ifndef OUTPUT
#define OUTPUT          1
#endif
#ifndef INPUT_PULLUP
#define INPUT_PULLUP    2
#endif

// 电平定义（兼容Arduino）
#ifndef LOW
#define LOW     0
#endif
#ifndef HIGH
#define HIGH    1
#endif


// 低内存模式开关
// #define PCF8574_LOW_MEMORY

// 低延迟模式开关
// #define PCF8574_LOW_LATENCY

// 编码器算法选择
#define BASIC_ENCODER_ALGORITHM

#ifdef PCF8574_LOW_LATENCY
    #define READ_ELAPSED_TIME 0
#else
    #define READ_ELAPSED_TIME 10
#endif

/**
 * @brief PCF8574数字输入结构体（非低内存模式）
 */
#ifndef PCF8574_LOW_MEMORY
struct DigitalInput {
    uint8_t p0;
    uint8_t p1;
    uint8_t p2;
    uint8_t p3;
    uint8_t p4;
    uint8_t p5;
    uint8_t p6;
    uint8_t p7;
};
#endif

/**
 * @brief PCF8574中断回调函数类型
 */
typedef std::function<void()> InterruptCallback;

/**
 * @brief ESP32 PCF8574 GPIO扩展器类
 * 
 * 这个类使用共享I2C实例和设备句柄进行高效通信
 */
class PCF8574 {
public:
    /**
     * @brief 带共享I2C的构造函数
     * @param i2c 共享I2C实例
     * @param address PCF8574设备地址
     * @param scl_speed_hz I2C速度，默认100kHz
     */
    PCF8574(std::shared_ptr<I2cMaster> i2c, uint8_t address, uint32_t scl_speed_hz = 100000);
    
    /**
     * @brief 带共享I2C和中断的构造函数
     * @param i2c 共享I2C实例
     * @param address PCF8574设备地址
     * @param interruptPin 中断引脚
     * @param interruptFunction 中断回调函数
     * @param scl_speed_hz I2C速度，默认100kHz
     */
    PCF8574(std::shared_ptr<I2cMaster> i2c, uint8_t address, uint8_t interruptPin, 
            InterruptCallback interruptFunction, uint32_t scl_speed_hz = 100000);
    
    /**
     * @brief 析构函数
     */
    ~PCF8574();
    
    // 禁用拷贝构造和赋值
    PCF8574(const PCF8574&) = delete;
    PCF8574& operator=(const PCF8574&) = delete;
    
    // ==================== Arduino兼容API ====================
    
    /**
     * @brief 初始化PCF8574
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t begin();
    
    /**
     * @brief 设置引脚模式
     * @param pin 引脚号 (0-7)
     * @param mode 模式 (INPUT, OUTPUT, INPUT_PULLUP)
     * @param output_start 输出模式的初始值 (LOW, HIGH)
     */
    void pinMode(uint8_t pin, uint8_t mode, uint8_t output_start = HIGH);
    
    /**
     * @brief 数字写入单个引脚
     * @param pin 引脚号 (0-7)
     * @param value 值 (LOW, HIGH)
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t digitalWrite(uint8_t pin, uint8_t value);
    
    /**
     * @brief 数字读取单个引脚
     * @param pin 引脚号 (0-7)
     * @param forceReadNow 是否强制立即读取
     * @return 引脚值 (LOW, HIGH)
     */
    uint8_t digitalRead(uint8_t pin, bool forceReadNow = false);
    
#ifndef PCF8574_LOW_MEMORY
    /**
     * @brief 读取所有引脚（结构体方式）
     * @return 数字输入结构体
     */
    DigitalInput digitalReadAll(void);
    
    /**
     * @brief 写入所有引脚（结构体方式）
     * @param digitalInput 数字输入结构体
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t digitalWriteAll(DigitalInput digitalInput);
#else
    /**
     * @brief 读取所有引脚（字节方式）
     * @return 8位值，每位对应一个引脚
     */
    uint8_t digitalReadAll(void);
    
    /**
     * @brief 写入所有引脚（字节方式）
     * @param digitalInput 8位值，每位对应一个引脚
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t digitalWriteAll(uint8_t digitalInput);
#endif
    
    /**
     * @brief 写入所有引脚（字节方式）
     * @param allpins 8位值，每位对应一个引脚
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t digitalWriteAllBytes(uint8_t allpins);
    
    // ==================== 编码器功能 ====================
    
    /**
     * @brief 设置编码器引脚
     * @param pinA 编码器A相引脚
     * @param pinB 编码器B相引脚
     */
    void encoder(uint8_t pinA, uint8_t pinB);
    
#ifdef BASIC_ENCODER_ALGORITHM
    /**
     * @brief 读取编码器值（基础算法，判断函数）
     * @param pinA 编码器A相引脚
     * @param pinB 编码器B相引脚
     * @param encoderValue 编码器值指针
     * @param reverseRotation 是否反向旋转
     * @return true: 有变化, false: 无变化
     */
    bool readEncoderValue(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation = false);
    
    /**
     * @brief 读取编码器值（返回增量）
     * @param pinA 编码器A相引脚
     * @param pinB 编码器B相引脚
     * @return 编码器增量值
     */
    int8_t readEncoderValue(uint8_t pinA, uint8_t pinB);
#endif
    
    // ==================== 中断功能 ====================
    
    /**
     * @brief 启用中断
     */
    void attachInterrupt();
    
    /**
     * @brief 禁用中断
     */
    void detachInterrupt();
    
    // ==================== 配置和状态 ====================
    
    /**
     * @brief 获取读取延迟
     * @return 延迟时间（毫秒）
     */
    int getLatency() const { return latency; }
    
    /**
     * @brief 设置读取延迟
     * @param latency 延迟时间（毫秒）
     */
    void setLatency(int latency = READ_ELAPSED_TIME) { this->latency = latency; }
    
    /**
     * @brief 获取传输状态码
     * @return 传输状态码
     */
    uint8_t getTransmissionStatusCode() const { return transmissionStatus; }
    
    /**
     * @brief 检查最后一次传输是否成功（判断函数）
     * @return true: 成功, false: 失败
     */
    bool isLastTransmissionSuccess() { return transmissionStatus == 0; }
    
    /**
     * @brief 检查设备是否在线（判断函数）
     * @return true: 在线, false: 离线
     */
    bool isOnline();
    
    /**
     * @brief 获取设备地址
     * @return 设备地址
     */
    uint8_t getAddress() const { return _address; }
    
    /**
     * @brief 获取设备句柄
     * @return 设备句柄
     */
    i2c_master_dev_handle_t getDeviceHandle() const { return _deviceHandle; }
    
    // ==================== ESP32扩展功能 ====================
    
    /**
     * @brief 扫描I2C总线上的PCF8574设备
     * @param foundDevices 输出找到的设备地址列表
     * @return 错误码，ESP_OK表示成功
     */
    esp_err_t scanDevices(std::vector<uint8_t>& foundDevices);
    
    /**
     * @brief 读取原始寄存器值
     * @return 原始8位寄存器值
     */
    uint8_t readRaw();
    
    /**
     * @brief 写入原始寄存器值
     * @param value 8位寄存器值
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t writeRaw(uint8_t value);
    
    /**
     * @brief 软复位设备（重新初始化所有引脚）
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t softReset();
    
    /**
     * @brief 获取引脚状态信息
     * @param pin 引脚号
     * @param isOutput 输出是否为输出模式
     * @param isHigh 输出是否为高电平
     * @param isPullup 输出是否启用上拉
     * @return ESP_OK: 成功, 其他: 失败
     */
    esp_err_t getPinInfo(uint8_t pin, bool& isOutput, bool& isHigh, bool& isPullup);
    
    /**
     * @brief 批量设置引脚模式
     * @param pinMask 引脚掩码
     * @param mode 模式
     * @param initialValue 初始值
     */
    void setPinModes(uint8_t pinMask, uint8_t mode, uint8_t initialValue = HIGH);

private:
    // I2C相关
    std::shared_ptr<I2cMaster> _i2c;
    i2c_master_dev_handle_t _deviceHandle = nullptr;  // 设备句柄，用于高效通信
    uint8_t _address;                       // 设备地址
    uint32_t _sclSpeedHz;                   // I2C频率
    
    // 中断相关
    bool _usingInterrupt = false;           // 是否使用中断
    uint8_t _interruptPin = 2;              // 中断引脚
    InterruptCallback _interruptFunction;   // 中断回调函数
    
    // 引脚模式管理
    uint8_t writeMode = 0b00000000;         // 输出模式掩码
    uint8_t writeModeUp = 0b00000000;       // 输出高电平掩码
    uint8_t readMode = 0b00000000;          // 输入模式掩码
    uint8_t readModePullUp = 0b00000000;    // 输入上拉掩码
    uint8_t readModePullDown = 0b00000000;  // 输入下拉掩码
    
    // 缓冲区管理
    uint8_t byteBuffered = 0b00000000;      // 读取缓冲区
    uint8_t writeByteBuffered = 0b00000000; // 写入缓冲区
    uint8_t resetInitial = 0b00000000;      // 初始重置值
    uint8_t initialBuffer = 0b00000000;     // 初始缓冲区值
    
    // 编码器相关
    volatile uint8_t encoderValues = 0b00000000; // 编码器值
    bool encoderPins[8] = {false};          // 编码器引脚标记
    
    // 时间和状态管理
    unsigned long lastReadMillis = 0;       // 上次读取时间
    int latency = READ_ELAPSED_TIME;        // 读取延迟
    uint8_t transmissionStatus = 0;         // 传输状态
    
#ifndef PCF8574_LOW_MEMORY
    DigitalInput digitalInput;              // 数字输入结构体
#endif
    
    // 内部方法
    esp_err_t readBuffer(bool force = true);
    void setVal(uint8_t pin, uint8_t value);
    
    // 中断处理
    static void IRAM_ATTR interruptHandler(void* arg);
    void handleInterrupt();
    
    // 工具函数
    inline uint8_t bit(uint8_t n) { return (1 << n); }
    unsigned long millis();
    bool isValidAddress(uint8_t address);   // 判断函数，保持bool
}; 